package cn.rwklyd.BookKeeping.filter;

import cn.rwklyd.BookKeeping.exception.CustomerAuthenticationException;
import cn.rwklyd.BookKeeping.handler.LoginFailureHandler;
import cn.rwklyd.BookKeeping.pojo.Permission;
import cn.rwklyd.BookKeeping.pojo.Result;
import cn.rwklyd.BookKeeping.pojo.User;
import cn.rwklyd.BookKeeping.pojo.vo.LoginUser;
import cn.rwklyd.BookKeeping.service.TokenService;
import cn.rwklyd.BookKeeping.util.JwtUtils;
import cn.rwklyd.BookKeeping.constant.MessageConstant;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.List;
import java.util.Map;

@Component
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    @Autowired
    private LoginFailureHandler loginFailureHandler;   // 登录失败处理器(表单验证使用)
    
    @Autowired
    private TokenService tokenService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 1、排除不需要验证的资源路径
        String uri = request.getRequestURI(); // 获取资源路径(并不是URL)
        // log.info("JWT过滤器处理请求: {}, 方法: {}", uri, request.getMethod());
        
        // 白名单路径直接放行
        if (uri.equals("/system/login") || uri.equals("/system/register") || uri.equals("/system/sendEmailCode")
                || uri.equals("/doc.html") || uri.startsWith("/swagger-ui/") || uri.startsWith("/v3/api-docs") ||
                uri.startsWith("/swagger-resources/") || uri.startsWith("/webjars/")) {
            log.info("白名单路径: {}, 直接放行", uri);
            filterChain.doFilter(request, response);  // 放行
            return;
        }

        //2、判断token是否为空
        String token = request.getHeader("Authorization");
        log.info("获取到的token：{}", token);
        if(!StringUtils.hasText(token)){
            log.warn("请求未携带token，返回未登录错误");
            loginFailureHandler.onAuthenticationFailure(request, response, new CustomerAuthenticationException(MessageConstant.USER_NOT_LOGIN));
            return;
//            throw new BaseException(MessageConstant.USER_NOT_LOGIN);
        }

        //3、校验令牌
        LoginUser loginUser = null;
        try {
            Claims claims = JwtUtils.parseJWT(token);
            Integer userId = claims.get("id", Integer.class);
            String username = claims.get("username", String.class);

            // 将 JSON 字符串反序列化为 List<Permission>
            String permissionsJson = claims.get("permissions", String.class);
            List<Permission> permissions = JSON.parseObject(permissionsJson, new TypeReference<List<Permission>>() {});
            
            log.debug("JWT令牌解析成功，用户ID: {}, 用户名: {}", userId, username);

            User user = new User();
            user.setId(userId);
            user.setUsername(username);
            loginUser = new LoginUser(user, permissions);
        } catch (ExpiredJwtException e) {
            log.info("访问令牌已过期，尝试刷新");
            
            try {
                // 尝试刷新访问令牌
                Result<Map<String, String>> refreshResult = tokenService.refreshAccessToken(token);
                
                if (refreshResult.getCode() == 200) {
                    // 刷新成功，获取新的访问令牌
                    String newToken = refreshResult.getData().get("accessToken");
                    
                    // 解析新的访问令牌
                    Claims claims = JwtUtils.parseJWT(newToken);
                    Integer userId = claims.get("id", Integer.class);
                    String username = claims.get("username", String.class);
                    String permissionsJson = claims.get("permissions", String.class);
                    List<Permission> permissions = JSON.parseObject(permissionsJson, new TypeReference<List<Permission>>() {});
                    
                    // 创建用户对象
                    User user = new User();
                    user.setId(userId);
                    user.setUsername(username);
                    loginUser = new LoginUser(user, permissions);
                    
                    // 在响应头中设置新的访问令牌
                    response.setHeader("Authorization", newToken);
                    // 允许前端访问自定义响应头
                    response.setHeader("Access-Control-Expose-Headers", "Authorization, Token-Refreshed, New-Token");
                    // 添加刷新标志，便于前端识别
                    response.setHeader("Token-Refreshed", "true");
                    // 额外添加一个新令牌头，确保前端能够接收到
                    response.setHeader("New-Token", newToken);
                    
                    log.info("访问令牌已刷新");
                } else {
                    // 刷新失败
                    log.error("刷新访问令牌失败: {}", refreshResult.getMsg());
                    loginFailureHandler.onAuthenticationFailure(request, response, 
                        new CustomerAuthenticationException(refreshResult.getCode() == 700 ? 
                            MessageConstant.REFRESH_TOKEN_EXPIRED : MessageConstant.TOKEN_EXPIRED));
                    return;
                }
            } catch (Exception ex) {
                log.error("JWT令牌刷新失败: {}", ex.getMessage());
                loginFailureHandler.onAuthenticationFailure(request, response, 
                    new CustomerAuthenticationException(MessageConstant.TOKEN_EXPIRED));
                return;
            }
        } catch (Exception e) {
            log.error("JWT令牌解析失败: {}", e.getMessage());
            loginFailureHandler.onAuthenticationFailure(request, response, 
                new CustomerAuthenticationException(MessageConstant.TOKEN_EXPIRED));
            return;
//            throw new BaseException(MessageConstant.TOKEN_EXPIRED);
        }

        //4、将对象储存到上下文中
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authentication);
        log.info("用户 {} 认证成功，已存入SecurityContext", loginUser.getUsername());

        filterChain.doFilter(request, response);  // 放行
    }
}
